// get url protocol
function TH_getURLScheme(url) {
	try {
		return Components.classes['@mozilla.org/network/io-service;1']
			.getService(Components.interfaces.nsIIOService)
			.extractScheme(url);
	} catch (e) {
		return null;
	}
}

// get main window
function TH_getMainWindow() {
	return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
		.getInterface(Components.interfaces.nsIWebNavigation)
		.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
		.rootTreeItem
		.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
		.getInterface(Components.interfaces.nsIDOMWindow);
}

// get selected browser window
function TH_getSelectedBrowserWindow() {
	var win = TH_getMainWindow();
	return win.gBrowser.selectedBrowser.contentWindow;
}

// execute event if scheme allowed
function TH_isSchemeAllowed(func) {
	var win = TH_getSelectedBrowserWindow();
	var scheme = TH_getURLScheme(win.location);

	if (scheme == 'http' || scheme == 'https' || scheme == 'ftp' || scheme == 'file' || scheme == 'data')
		func(scheme, win);
	else
		throw 'scheme not supported';
}

// get string bundle
function TH_getStringBundle(url) {
	var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'].getService(Components.interfaces.nsILocaleService);
	var stringBundleService = Components.classes['@mozilla.org/intl/stringbundle;1'].getService(Components.interfaces.nsIStringBundleService);
	
	var appLocale = localeService.getApplicationLocale();
	return stringBundleService.createBundle(url, appLocale);
}

// get all bookmarks thanks to Steve Yegge; http://steve-yegge.blogspot.com
function TH_getBookmarkList(doSort) {
	var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"].
		getService(Components.interfaces.nsIRDFService);
		
	var bmks = rdf.GetDataSource("rdf:bookmarks");
	var NC_NS = "http://home.netscape.com/NC-rdf#";
	var kwArc = rdf.GetResource(NC_NS + "ShortcutURL");
	var urlArc = rdf.GetResource(NC_NS + "URL");
	var nameArc = rdf.GetResource(NC_NS + "Name");
	var rdfLiteral = Components.interfaces.nsIRDFLiteral;
	var e = bmks.GetAllResources();
	var items = [];
	
	while(e.hasMoreElements()) {
		var r =  e.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
		var urlR = bmks.GetTarget(r, urlArc, true);
		var kwR = bmks.GetTarget(r, kwArc, true);
		var nameR = bmks.GetTarget(r, nameArc, true);
		
		if (!(nameR && urlR)) {
			continue;
		}
		
		var item = {};
		item.name = nameR.QueryInterface(rdfLiteral).Value;
		item.url = urlR.QueryInterface(rdfLiteral).Value;
		item.kw = '';
		
		if (kwR) {
			item.kw = kwR.QueryInterface(rdfLiteral).Value;
		}
		
		items.push(item);
	}
	
	if (doSort) {
		items.sort(function(a, b) {
			return (a.name.upcase() < b.name.upcase()) ? -1 : 1;
		});
	}
	
	return items;
}

// check if URL is bookmarklet
function TH_isURLBookmarklet(url) {
	if (url.substring(0, 11) == 'javascript:')
		return true;
		
	return false;
}

// wrap function
function TH_frap(f) {
	var a = [];

	for (var i = 1; i < arguments.length; i++) {
		a.push(arguments[i]);
	}

	return function () {
		f.apply(f, a);
	};
}

// extend
function TH_extend(o, p) {
	for (var i in p) {
		o[i] = p[i];
	}
	
	return o;
};

// get simple *trusted* sandbox
function TH_getSandbox(win, scope) {
	var sandbox = new Components.utils.Sandbox(win);

	var scope = TH_extend({
		window: TH_getSelectedBrowserWindow(),
		document: TH_getSelectedBrowserWindow().document,

		_window: TH_getMainWindow(),
		_document: TH_getMainWindow().document,

		XMLHttpRequest: XMLHttpRequest,

		include: function (url) {
			var script = TH_readURL(url);
			TH_evalInSandbox(script, url, sandbox);
		},
	}, scope);

	return TH_extend(sandbox, scope);
}

// eval in sandbox
function TH_evalInSandbox(code, codebase, sandbox) {
	if (Components.utils && Components.utils.Sandbox) {
		Components.utils.evalInSandbox(code, sandbox);
	} else if (Components.utils && Components.utils.evalInSandbox) {
		Components.utils.evalInSandbox(code, codebase, sandbox);
	} else if (Sandbox) {
		evalInSandbox(code, sandbox, codebase);
	} else {
		throw 'cannot eval';
	}
}

// get pref
function TH_getPref(name, def) {
	var prefs = Components.classes['@mozilla.org/preferences-service;1']
		.getService(Components.interfaces.nsIPrefBranch);

	try {
		return prefs.getCharPref(name);
	} catch (e) {
		return def;
	}
}

// set pref
function TH_setPref(name, val) {
	var prefs = Components.classes['@mozilla.org/preferences-service;1']
		.getService(Components.interfaces.nsIPrefBranch);

	prefs.setCharPref(name, val);
}

// expand macros
function TH_expandMacros(str) {
	var tokens = str.split(/(\$\{\w+\})/g)

	for (var i = 0; i < tokens.length; i++) {
		switch (tokens[i]) {
			case '${url}':
				tokens[i] = TH_getSelectedBrowserWindow().location;
				break;

			case '${timestamp}':
				tokens[i] = new Date().getTime();
				break;
		}
	}

	return tokens.join('');
}

// read file
function TH_readFile(path) {
	var data = '';

	var file = Components.classes['@mozilla.org/file/local;1']
		.createInstance(Components.interfaces.nsILocalFile);

	file.initWithPath(path);

	var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
		.createInstance(Components.interfaces.nsIFileInputStream);

	var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
		.createInstance(Components.interfaces.nsIScriptableInputStream);

	fstream.init(file, 0x01, 0, 0);
	sstream.init(fstream); 

	var str = sstream.read(4096);

	while (str.length > 0) {
		data += str;
		str = sstream.read(4096);
	}

	sstream.close();
	fstream.close();

	return data;
}

// write to file
function TH_writeFile(path, data) {
	var aFile = Components.classes['@mozilla.org/file/local;1']
		.createInstance(Components.interfaces.nsILocalFile);

	aFile.initWithPath(path);
	aFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 600);

	var stream = Components.classes['@mozilla.org/network/safe-file-output-stream;1']
		.createInstance(Components.interfaces.nsIFileOutputStream);

	stream.init(aFile, 0x04 | 0x08 | 0x20, 0600, 0); // write, create, truncate

	var data = data.toString();

	stream.write(data, data.length);

	if (stream instanceof Components.interfaces.nsISafeOutputStream) {
		stream.finish();
	} else {
		stream.close();
	}
}

// read URL
function TH_readURL(url) {
	var q = new XMLHttpRequest();
	q.open('GET', url, false);
	q.send(null);

	if (q.status != 200 && q.status != 0) {
		throw 'unable to get URL';
	}

	return q.responseText;
}

// get OS
function TH_getOS() {
	var rv;

	var np = navigator.platform;
	var ua = navigator.userAgent;

	if (np.indexOf('Win') == 0) {
		rv = 'win32';
	} else if (np.indexOf('Linux') == 0) {
		rv = 'linux';
	} else if (ua.indexOf('Mac OS X') != -1) {
		rv = 'macosx';
	} else if (np.indexOf('Mac') != -1) {
		rv = 'macos';
	} else {
		rv = null;
	}

	return rv;
}

// get directroy separator
function TH_getDirSeparator() {
	var rv;

	switch (TH_getOS()) {
		case 'win32':
			rv = '\\';
			break;

		case 'linux':
		case 'macosx':
			rv = '/';
			break;

		case 'macos':
			rv = ':';
			break;

		default:
			rv = null;
	}

	return rv;
}

// parse query
function TH_parseQuery(query) {
	var queryobj = new Object();
	var tokens = query.split('&');
	
	for (var index = 0; index < tokens.length; index++) {
		var pair = tokens[index].split('=');
		queryobj[unescape(pair[0])] = unescape(pair[1]);
	}
		
	return queryobj;
};

// parse URL
function TH_parseURL(url) {
	var REGEX = /^((\w+):\/\/)?((\w+):?(\w+)?@)?([^\/\?:]+):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(\w*)/;

	var fields = {
		'href': 0,
		'username': 4,
		'password': 5,
		'port': 7,
		'protocol': 2,
		'host': 6,
		'hostname': 6,
		'pathname': 8,
		'search': 9,
		'hash': 10};

	var result = new Object();
	var r = REGEX.exec(url);

	for (var field in fields) {
		result[field] = r[fields[field]];
	}

	result.protocol = result.protocol?result.protocol:'';
	result.hash = result.hash?'#' + result.hash:'#';
	result.username = result.username?result.username:'';
	result.password = result.password?result.password:'';
	result.search = result.search?'?' + result.search:'?';
	result.pathname = result.pathname?result.pathname:'/';

	if (result.port == undefined) {
		switch (result.protocol) {
			case 'http':
				result.port = 80;
				break;

			case 'https':
				result.port = 443;
				break;

			case 'ftp':
				result.port = 21;
				break;

			default:
				result.port = '';
				break;
		}
	}

	return result;
}

// build URL
function TH_buildURL(o) {
	var host = o.host ? o.host : (o.hostname ? o.hostname : null);

	if (!host) {
		return '';
	}

	var protocol = o.protocol ? (o.protocol.match(/http|https|ftp|file|chrome/) ? o.protocol + '://' : o.protocol) : 'http://';

	var username = o.username ? o.username : '';
	var password = o.password ? o.password : '';
	var creds = (username || password) ? username + ':' + password + '@' : '';

	var port = o.port ? ':' + o.port : '';

	var pathname = o.pathname ? (o.pathname[0] == '/' ? o.pathname : '/' + o.pathname) : '/';
	var search = o.search ? (o.search[0] == '?' ? o.search : '?' + o.search) : '';
	var hash = o.hash ? (o.hash[0] == '#' ? o.hash : '#' + o.hash) : '';

	return protocol + creds + host + (port != ':80' ? port : '') + pathname + (search != '?' ? search : '') + (hash != '#' ? hash : '');
}

// escape HTML
function TH_escapeHTML(s) {
	return s.replace(/\&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;');
}

// HTMLize string
function TH_htmlizeString(s) {
	return s.replace(/\n/g, '<br/>')
		.replace(/\s/g, '&nbsp;')
		.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;');
}

// trim string
function TH_trimString(s) {
	return s.replace(/^\s+|\s+$/, '');
}

// expand variables, @ for local and @@ for global
function TH_expandVars(obj) {
	var new_obj = {};

	for (var i in obj) {
		if (obj[i].substring(0, 2) == '@@') {
			obj[i] = eval('TH_getMainWindow().' + obj[i].substring(2));
		} else if (obj[i].substring(0, 1) == '@') {
			obj[i] = eval('TH_getSelectedBrowserWindow().' + obj[i].substring(1));
		}

		if (i.substring(0, 2) == '@@') {
			eval('TH_getMainWindow().' + i.substring(2) + ' = obj[i]');
		} else if (i.substring(0, 1) == '@') {
			eval('TH_getSelectedBrowserWindow().' + i.substring(1) + ' = obj[i]');
		} else {
			new_obj[i] = obj[i];
		}
	}

	return new_obj;
}

// expand local backtricks
function TH_expandLocalBackticks(code) {
	var script = '                                                                 \
		(new function () {                                                     \
			return function (self) {                                       \
				for (var i in self) {                                  \
					if (typeof(self[i]) == "string") {             \
						var t = self[i].split(/(`.*?`)/);      \
										       \
						for (var z = 0; z < t.length; z++) {   \
							var m = t[z].match(/`(.*?)`/); \
										       \
							if (m) {                       \
								t[z] = eval(m[1]);     \
							}                              \
						}                                      \
										       \
						self[i] = t.join("");                  \
					}                                              \
				}                                                      \
			};                                                             \
		})(this);                                                              \
	';

	return script + code;
}
